#!/usr/bin/php -q
<?php
// Try to load Net_SmartIRC and error and die if we can't
if ( ! require_once( 'Net/SmartIRC.php' ) ) {
	die( 'wp-svn-bot requires the Net_SmartIRC pear package. Please install this package and restart the bot.' );
}

// Try to load the config file and error and die if we can't
if ( ! require_once( 'svn-bot-config.php' ) ) {
	die( 'Could not include the svn-bot-config.php config file. Please create this file from trac-bot-config-sample.php and restart the bot.' );
}

/* Version of this bot */
define( 'VERSION', '1.1' );

// http://www.christopherbloom.com/2011/02/24/find-the-longest-common-substring-using-php/
function sort_by_strlen( $a, $b ) {
	return ( strlen( $a ) == strlen( $b ) ) ? strcmp( $a, $b ) : ( strlen( $a ) < strlen( $b ) ) ? -1 : 1;
}

function longest_common_substring( $words ) {
	$words = array_map( 'strtolower', array_map( 'trim', $words ) );
	usort( $words, 'sort_by_strlen' );
	$longest_common_substring = array();
	$shortest_string = str_split( array_shift( $words ) );
	while ( ! empty( $shortest_string ) ) {
		array_unshift( $longest_common_substring, '' );
		foreach ( $shortest_string as $ci => $char ) {
			foreach ( $words as $wi => $word )
				if ( ! strstr( $word, $longest_common_substring[0] . $char ) )
					break 2;
			$longest_common_substring[0].= $char;
		}
		array_shift($shortest_string);
	}
	usort( $longest_common_substring, 'sort_by_strlen' );
	return array_pop($longest_common_substring);
}

class wpsvn {

	function __construct() {
		global $svn_hosts;
		$this->svn = $svn_hosts;
	}

	function logs( &$irc ) {
		foreach ( $this->svn as $channel => $svninfos ) {
			if ( ! $irc->isJoined( $channel ) )
				continue;

			foreach ( $svninfos as $svninfo ) {

				$svn_url = $svninfo['url'];
				$project = $svninfo['project'];
				$trac_url = isset( $svninfo['trac'] ) ? $svninfo['trac'] : null;

				$head = svn_log( $svn_url, SVN_REVISION_HEAD );
				$head_rev = (int) $head[0]['rev'];

				if ( ! file_exists( dirname( __FILE__ ) . "/$project.rev" ) )
					continue;

				$last_rev = (int) trim( file_get_contents( dirname( __FILE__ ) . "/$project.rev" ) );

				if ( empty($last_rev) || $head_rev <= $last_rev )
					continue;

				$new_revs = svn_log( $svn_url, $last_rev + 1, $head_rev );
				foreach ( $new_revs as $rev ) {
					$paths = array();
					foreach ( $rev['paths'] as $path ) {
						$paths[] = $path['path'];
					}
					$dir = preg_replace( '%/[^/]+$%', '/', longest_common_substring( $paths ) );
					$trimpaths = $dirnames = $files = array();
					foreach ( $paths as $path ) {
						$trimpaths[] = str_replace( $dir, '', $path );
						$dirname = dirname( str_replace( $dir, '', $path ) );
						if ( ! in_array( $dirname, array( '', '.', '/' ) ) ) {
							$dirnames[] = $dirname;
							$file = trim( str_replace( dirname( str_replace( $dir, '', $path ) ), '', str_replace( $dir, '', $path ) ), '/' );
							if ( ! empty( $file ) )
								$files[] = $file;
						} else {
							$file = trim( str_replace( $dir, '', $path ), '/' );
							if ( ! empty( $file ) )
								$files[] = $file;
						}
					}

					$rev_url = isset( $trac_url ) ? sprintf($trac_url, $rev['rev']) : "r{$rev['rev']}";
					$dirnames = array_unique( (array) $dirnames );
					$output  = "$project: {$rev['author']} * $rev_url $dir";

					if ( empty( $dirnames ) && count( $files ) == 1 ) {
						$output .= $trimpaths[0];
					} elseif ( !empty( $dirnames ) && ! empty( $files ) ) {
						$output .= " (" . count( $files ) . " files in " . count( $dirnames ) . " dirs)";
					} elseif ( empty( $dirnames ) && ! empty( $files ) && count( $files ) < 7 ) {
						$output .= " (" . implode( " ", $files ) . ")";
					} elseif ( empty( $dirnames ) && ! empty( $files ) && count( $files ) >= 7 ) {
						$output .= " (" . count( $files ) . " files)";
					}

					$msgs = explode( "\n", trim( $rev['msg'] ) );
					if ( count( $msgs ) == 1 ) {
						$output .= ": {$msgs[0]}";
						unset( $msgs[0] );
					} else {
						$output .= ":";
					}
					$irc->message( SMARTIRC_TYPE_CHANNEL, $channel, trim( $output ) );

					if ( ! empty( $msgs ) )
						foreach ( $msgs as $msg )
							if ( ! empty( $msg ) )
								$irc->message( SMARTIRC_TYPE_CHANNEL, $channel, "$project: " . trim( $msg ) );

					file_put_contents( dirname( __FILE__ ) . "/$project.rev", $rev['rev'] );
				}
			}
		}
	}
}

class admin {
	function is_admin( $data ) {
		global $bot_admins;
		if ( isset( $bot_admins[$data->nick] ) && $bot_admins[$data->nick] == $data->host )
			return true;
		else
			return false;
	}

	function join( $irc, $data ) {
		global $irc_channels;
		if ( ! $this->is_admin( $data ) )
			return;

		list( $command, $query ) = explode( ' ', $data->message );

		if ( strstr( $query, ':' ) ) {
			list( $channel, $require_nick ) = explode( ':', $query );
			$require_nick = ( $require_nick == 'true' ) ? true : false;
		} else {
			$channel = $query;
			$require_nick = true;
		}

		if ( ! $irc->isJoined( $channel ) ) {
			$irc->message( SMARTIRC_TYPE_QUERY, $data->nick, "Joining $channel" );
			$irc->join( array( $channel ) );
			$irc_channels[$channel] = $require_nick;
		} else {
			$irc->message( SMARTIRC_TYPE_QUERY, $data->nick, "Already joined to $channel" );
		}
	}

	function part( $irc, $data ) {
		global $irc_channels;
		if ( ! $this->is_admin( $data ) )
			return;

		list( $command, $query ) = explode( ' ', $data->message );

		if ( $irc->isJoined( $query ) ) {
			$irc->message( SMARTIRC_TYPE_QUERY, $data->nick, "Parting $query" );
			$irc->part( array( $query ) );
			unset( $irc_channels[$query] );
		} else {
			$irc->message( SMARTIRC_TYPE_QUERY, $data->nick, "Not joined to $query" );
		}
	}

	function nick( $irc, $data ) {
		if ( ! $this->is_admin( $data ) )
			return;

		list( $command, $query ) = explode( ' ', $data->message );
		
		if ( ! $irc->isMe( $query ) ) {
			$irc->message( SMARTIRC_TYPE_QUERY, $data->nick, "Changing nick to $query" );
			$irc->changeNick( $query );
		} else {
			$irc->message( SMARTIRC_TYPE_QUERY, $data->nick, "Nick is already $query" );
		}
	}

	function quit( $irc, $data ) {
		if ( ! $this->is_admin( $data ) )
			return;

		$irc->message( SMARTIRC_TYPE_QUERY, $data->nick, "Quitting" );
		$irc->quit( 'Quit Requested', SMARTIRC_CRITICAL );
	}

	function ping( $irc, $data ) {
		$irc->message( SMARTIRC_TYPE_QUERY, $data->nick, "pong" );
	}

	function where( $irc, $data ) {
		$irc->message(SMARTIRC_TYPE_QUERY, $data->nick, 'Channels: ' . implode(',', array_keys($irc->_channels)));
	}
}

$svn = &new wpsvn();
$admin = &new admin();
$irc = &new Net_SmartIRC();

$irc->setDebug(SMARTIRC_DEBUG_ALL);
$irc->setAutoReconnect( true );
$irc->setAutoRetry( true );
$irc->setUseSockets( true );
$irc->setCtcpVersion( BOT_DESCRIPTION . ' v' . VERSION );
$irc->setChannelSyncing( true );

$irc->registerActionhandler( SMARTIRC_TYPE_QUERY, '/^part\ #.+/i', $admin, 'part' );
$irc->registerActionhandler( SMARTIRC_TYPE_QUERY, '/^join\ #.+/i', $admin, 'join' );
$irc->registerActionhandler( SMARTIRC_TYPE_QUERY, '/^quit$/i', $admin, 'quit' );
$irc->registerActionhandler( SMARTIRC_TYPE_QUERY, '/^nick\ .+/i', $admin, 'nick' );
$irc->registerActionhandler( SMARTIRC_TYPE_QUERY, '/^ping$/', $admin, 'ping' );
$irc->registerActionhandler( SMARTIRC_TYPE_QUERY, '/^where$/', $admin, 'where' );

$irc->registerTimehandler( 60000, $svn, 'logs' );

$irc->connect( IRC_HOST, IRC_PORT );
$irc->login( IRC_NICK, IRC_REALNAME, IRC_USERMODE, IRC_USERNAME, IRC_PASSWORD );
sleep( 5 ); // Sleep before joining channels
$irc->join( $irc_channels );
$irc->listen();
$irc->disconnect();
